```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>缓存问题全解析：穿透、击穿与雪崩</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            background-color: #f9fafb;
            color: #1f2937;
            line-height: 1.6;
        }
        .hero {
            background: linear-gradient(135deg, #6b21a8 0%, #9333ea 100%);
        }
        .hero-title {
            text-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .card {
            transition: all 0.3s ease;
            box-shadow: 0 4px 6px rgba(0,0,0,0.05);
        }
        .card:hover {
            transform: translateY(-4px);
            box-shadow: 0 10px 15px rgba(0,0,0,0.1);
        }
        .section-title {
            position: relative;
            padding-left: 1.5rem;
        }
        .section-title:before {
            content: "";
            position: absolute;
            left: 0;
            top: 0.5rem;
            height: 1.5rem;
            width: 0.5rem;
            background: #8b5cf6;
            border-radius: 0.25rem;
        }
        .diagram-container {
            background-color: white;
            border-radius: 0.5rem;
            padding: 1.5rem;
            box-shadow: 0 4px 6px rgba(0,0,0,0.05);
        }
        .code-block {
            font-family: monospace;
            background-color: #1e1e1e;
            color: #d4d4d4;
            border-radius: 0.375rem;
        }
        .solution-tag {
            display: inline-flex;
            align-items: center;
            padding: 0.25rem 0.75rem;
            border-radius: 9999px;
            font-size: 0.875rem;
            font-weight: 500;
        }
    </style>
</head>
<body class="min-h-screen">
    <!-- Hero Section -->
    <section class="hero text-white py-16 px-4 md:py-24">
        <div class="container mx-auto max-w-5xl">
            <div class="flex flex-col items-center text-center">
                <span class="mb-4 inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-purple-100 text-purple-800">
                    <i class="fas fa-database mr-2"></i> 数据库性能优化
                </span>
                <h1 class="hero-title text-4xl md:text-5xl font-bold leading-tight mb-4">缓存问题全解析</h1>
                <p class="text-xl md:text-2xl text-purple-100 max-w-3xl mb-8">
                    穿透、击穿与雪崩的深度剖析与解决方案
                </p>
                <div class="flex flex-wrap justify-center gap-4">
                    <div class="flex items-center">
                        <i class="fas fa-shield-alt mr-2"></i>
                        <span>安全防护</span>
                    </div>
                    <div class="flex items-center">
                        <i class="fas fa-bolt mr-2"></i>
                        <span>高性能</span>
                    </div>
                    <div class="flex items-center">
                        <i class="fas fa-chart-line mr-2"></i>
                        <span>优化策略</span>
                    </div>
                </div>
            </div>
        </div>
    </section>

    <!-- Main Content -->
    <main class="container mx-auto max-w-5xl px-4 py-12">
        <!-- Introduction Card -->
        <div class="card bg-white rounded-xl p-6 mb-12">
            <div class="flex items-start">
                <div class="flex-shrink-0 mr-4 text-purple-600">
                    <i class="fas fa-info-circle text-2xl"></i>
                </div>
                <div>
                    <h2 class="text-xl font-bold mb-2">缓存问题概述</h2>
                    <p class="text-gray-600">
                        在高并发系统中，缓存是提升性能的关键组件。然而，不当的缓存使用可能导致严重的性能问题甚至系统崩溃。
                        本文将深入分析三种常见的缓存问题：缓存穿透、缓存击穿和缓存雪崩，并提供切实可行的解决方案。
                    </p>
                </div>
            </div>
        </div>

        <!-- Cache Penetration Section -->
        <section class="mb-16">
            <h2 class="section-title text-3xl font-bold mb-8">1. 缓存穿透</h2>
            
            <div class="grid md:grid-cols-2 gap-8 mb-8">
                <div class="card bg-white rounded-xl p-6">
                    <h3 class="text-xl font-bold mb-4 flex items-center">
                        <span class="w-8 h-8 bg-purple-100 text-purple-800 rounded-full flex items-center justify-center mr-3">1</span>
                        出现原理
                    </h3>
                    <p class="text-gray-600 mb-4">
                        key对应的数据在数据库并不存在，每次针对此key的请求从缓存获取不到，请求都会到数据源，从而可能压垮数据源。
                    </p>
                    <p class="text-gray-600">
                        比如用一个不存在的商品id获取商品信息，不论缓存还是数据库都没有，若黑客利用此漏洞进行攻击可能压垮数据库。
                    </p>
                </div>
                
                <div class="diagram-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597854323-00b0040e-b701-4e1c-b568-9a0998fb0e7e.png" alt="缓存穿透示意图" class="rounded-lg w-full h-auto">
                </div>
            </div>
            
            <h3 class="text-2xl font-bold mb-6 text-gray-800">解决方案</h3>
            
            <div class="grid md:grid-cols-3 gap-6">
                <!-- Solution 1 -->
                <div class="card bg-white rounded-xl p-6">
                    <div class="flex items-center mb-3">
                        <span class="solution-tag bg-purple-100 text-purple-800">
                            <i class="fas fa-clock mr-1"></i> 方案1
                        </span>
                    </div>
                    <h4 class="font-bold text-lg mb-2">设置空值</h4>
                    <p class="text-gray-600 mb-4">
                        如果从数据库查询的对象为空，也放入缓存，只是设定的缓存过期时间较短，比如设置为60秒。
                    </p>
                    <div class="diagram-container p-0 overflow-hidden">
                        <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597854736-79a3eed2-59a5-4c6f-b51b-2d8429ba5306.png" alt="设置空值解决方案" class="w-full h-auto">
                    </div>
                </div>
                
                <!-- Solution 2 -->
                <div class="card bg-white rounded-xl p-6">
                    <div class="flex items-center mb-3">
                        <span class="solution-tag bg-blue-100 text-blue-800">
                            <i class="fas fa-shield-alt mr-1"></i> 方案2
                        </span>
                    </div>
                    <h4 class="font-bold text-lg mb-2">拦截机制</h4>
                    <p class="text-gray-600">
                        如用户鉴权校验，id做基础校验，id<=0的直接拦截；
                    </p>
                    <div class="mt-4 p-4 bg-gray-50 rounded-lg">
                        <pre class="code-block text-sm p-4 rounded">
if (id <= 0) {
    return "非法请求";
}</pre>
                    </div>
                </div>
                
                <!-- Solution 3 -->
                <div class="card bg-white rounded-xl p-6">
                    <div class="flex items-center mb-3">
                        <span class="solution-tag bg-green-100 text-green-800">
                            <i class="fas fa-filter mr-1"></i> 方案3
                        </span>
                    </div>
                    <h4 class="font-bold text-lg mb-2">布隆过滤器</h4>
                    <p class="text-gray-600 mb-4">
                        可以判断某样东西一定不存在或者可能存在。bloomfilter就类似于一个hash set，用于快速判某个元素是否存在于集合中。
                    </p>
                    <div class="diagram-container p-0 overflow-hidden">
                        <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597855176-dd8b13ea-9a4a-4a0d-97d0-6eced00de0dd.png" alt="布隆过滤器示意图" class="w-full h-auto">
                    </div>
                    <div class="mt-4 p-4 bg-gray-50 rounded-lg">
                        <pre class="code-block text-sm p-4 rounded">
System.out.println("ABCDEa123abc".hashCode());  // 165374702
System.out.println("ABCDFB123abc".hashCode());  // 165374702</pre>
                    </div>
                </div>
            </div>
        </section>

        <!-- Cache Breakdown Section -->
        <section class="mb-16">
            <h2 class="section-title text-3xl font-bold mb-8">2. 缓存击穿</h2>
            
            <div class="grid md:grid-cols-2 gap-8 mb-8">
                <div class="card bg-white rounded-xl p-6">
                    <h3 class="text-xl font-bold mb-4 flex items-center">
                        <span class="w-8 h-8 bg-blue-100 text-blue-800 rounded-full flex items-center justify-center mr-3">2</span>
                        出现原理
                    </h3>
                    <p class="text-gray-600">
                        缓存击穿是指缓存中没有但数据库中有的数据（一般是缓存时间到期），这时由于并发用户特别多，
                        同时读缓存没读到数据，又同时去数据库去取数据，引起数据库压力瞬间增大，造成过大压力。
                    </p>
                </div>
                
                <div class="diagram-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597855640-730d8a9c-79b9-4959-ae0a-feabdeaff8e8.png" alt="缓存击穿示意图" class="rounded-lg w-full h-auto">
                </div>
            </div>
            
            <h3 class="text-2xl font-bold mb-6 text-gray-800">解决方案</h3>
            
            <div class="grid md:grid-cols-2 gap-6">
                <!-- Solution 1 -->
                <div class="card bg-white rounded-xl p-6">
                    <div class="flex items-center mb-3">
                        <span class="solution-tag bg-yellow-100 text-yellow-800">
                            <i class="fas fa-fire mr-1"></i> 方案1
                        </span>
                    </div>
                    <h4 class="font-bold text-lg mb-2">热点数据永不过期</h4>
                    <p class="text-gray-600">
                        设置热点数据永远不过期(爆款商品访问次数最多)，避免因缓存失效导致的数据库压力。
                    </p>
                </div>
                
                <!-- Solution 2 -->
                <div class="card bg-white rounded-xl p-6">
                    <div class="flex items-center mb-3">
                        <span class="solution-tag bg-red-100 text-red-800">
                            <i class="fas fa-lock mr-1"></i> 方案2
                        </span>
                    </div>
                    <h4 class="font-bold text-lg mb-2">锁机制</h4>
                    <p class="text-gray-600">
                        使用互斥锁，当缓存失效时，只允许一个请求去查询数据库并更新缓存，其他请求等待或使用旧数据。
                    </p>
                </div>
            </div>
        </section>

        <!-- Cache Avalanche Section -->
        <section class="mb-16">
            <h2 class="section-title text-3xl font-bold mb-8">3. 缓存雪崩</h2>
            
            <div class="grid md:grid-cols-2 gap-8 mb-8">
                <div class="card bg-white rounded-xl p-6">
                    <h3 class="text-xl font-bold mb-4 flex items-center">
                        <span class="w-8 h-8 bg-red-100 text-red-800 rounded-full flex items-center justify-center mr-3">3</span>
                        出现原理
                    </h3>
                    <p class="text-gray-600 mb-4">
                        缓存雪崩是指缓存中数据大批量到过期时间，而查询数据量巨大，引起数据库压力过大甚至down机。
                        和缓存击穿不同的是，缓存击穿指并发查同一条数据，缓存雪崩是不同数据都过期了，很多数据都查不到从而查数据库。
                    </p>
                    <p class="text-gray-600">
                        比如，马上就要到双十二零点，很快就会迎来一波抢购，这波商品时间比较集中的放入了缓存，假设缓存一个小时。
                        那么到了凌晨一点钟的时候，这批商品的缓存就都过期了。而对这批商品的访问查询，都落到了数据库上，对于数据库而言，就会产生周期性的压力波峰。
                    </p>
                </div>
                
                <div class="diagram-container">
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1652597856127-251c05b9-c758-4e07-86c8-5170a0c84ad8.png" alt="缓存雪崩示意图" class="rounded-lg w-full h-auto">
                </div>
            </div>
            
            <h3 class="text-2xl font-bold mb-6 text-gray-800">解决方案</h3>
            
            <div class="grid md:grid-cols-2 gap-6">
                <!-- Solution 1 -->
                <div class="card bg-white rounded-xl p-6">
                    <div class="flex items-center mb-3">
                        <span class="solution-tag bg-indigo-100 text-indigo-800">
                            <i class="fas fa-random mr-1"></i> 方案1
                        </span>
                    </div>
                    <h4 class="font-bold text-lg mb-2">随机过期时间</h4>
                    <p class="text-gray-600">
                        缓存数据的过期时间设置随机，防止同一时间大量数据过期现象发生。
                    </p>
                </div>
                
                <!-- Solution 2 -->
                <div class="card bg-white rounded-xl p-6">
                    <div class="flex items-center mb-3">
                        <span class="solution-tag bg-green-100 text-green-800">
                            <i class="fas fa-fire mr-1"></i> 方案2
                        </span>
                    </div>
                    <h4 class="font-bold text-lg mb-2">热点数据永不过期</h4>
                    <p class="text-gray-600">
                        对于特别热门的数据，可以设置为永不过期，由后台服务定期更新缓存。
                    </p>
                </div>
            </div>
        </section>

        <!-- Additional Sections -->
        <div class="grid md:grid-cols-2 gap-8">
            <!-- Cache Warm-up -->
            <section class="card bg-white rounded-xl p-6">
                <h2 class="section-title text-2xl font-bold mb-4">4. 缓存预热</h2>
                <p class="text-gray-600 mb-4">
                    缓存预热就是系统上线后，将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候，先查询数据库，然后再将数据缓存的问题！用户直接查询事先被预热的缓存数据！
                </p>
                
                <h3 class="font-bold text-lg mb-2 text-gray-800">解决思路：</h3>
                <ul class="list-disc pl-5 text-gray-600 space-y-2">
                    <li>直接写个缓存刷新页面，上线时手工操作下</li>
                    <li>数据量不大，可以在项目启动的时候自动进行加载</li>
                    <li>定时刷新缓存</li>
                </ul>
            </section>
            
            <!-- Cache Update -->
            <section class="card bg-white rounded-xl p-6">
                <h2 class="section-title text-2xl font-bold mb-4">5. 缓存更新</h2>
                <p class="text-gray-600 mb-4">
                    缓存更新除了缓存服务器自带的缓存失效策略之外，我们还可以根据具体的业务需求进行自定义的缓存淘汰。
                </p>
                
                <h3 class="font-bold text-lg mb-2 text-gray-800">常见策略：</h3>
                <div class="space-y-4">
                    <div>
                        <h4 class="font-medium mb-1">1. 定时清理</h4>
                        <p class="text-gray-600 text-sm">
                            定时去清理过期的缓存；维护大量缓存的key比较麻烦
                        </p>
                    </div>
                    <div>
                        <h4 class="font-medium mb-1">2. 惰性清理</h4>
                        <p class="text-gray-600 text-sm">
                            当有用户请求过来时，再判断缓存是否过期，过期就去底层系统得到新数据并更新缓存。逻辑相对复杂
                        </p>
                    </div>
                </div>
            </section>
        </div>

        <!-- Cache Eviction Strategies -->
        <section class="mt-12">
            <h2 class="section-title text-3xl font-bold mb-6">6. 缓存淘汰策略</h2>
            
            <div class="card bg-white rounded-xl p-6 overflow-hidden">
                <div class="overflow-x-auto">
                    <table class="min-w-full divide-y divide-gray-200">
                        <thead class="bg-gray-50">
                            <tr>
                                <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">策略</th>
                                <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">描述</th>
                            </tr>
                        </thead>
                        <tbody class="bg-white divide-y divide-gray-200">
                            <tr>
                                <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">Volatile-lru</td>
                                <td class="px-6 py-4 text-sm text-gray-500">只限于设置了 expire 的部分; 优先删除最近最少使用的 key</td>
                            </tr>
                            <tr>
                                <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">Allkeys-lru</td>
                                <td class="px-6 py-4 text-sm text-gray-500">所有key通用; 优先删除最近最少使用的 key</td>
                            </tr>
                            <tr>
                                <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">Volatile-random</td>
                                <td class="px-6 py-4 text-sm text-gray-500">采用随机淘汰策略删除超时的键值对</td>
                            </tr>
                            <tr>
                                <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">Allkeys-random</td>
                                <td class="px-6 py-4 text-sm text-gray-500">只限于设置了 expire 的部分; 随机删除一部分 key</td>
                            </tr>
                            <tr>
                                <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">Volatile-ttl</td>
                                <td class="px-6 py-4 text-sm text-gray-500">只限于设置了 expire 的部分; 优先删除剩余时间短的key</td>
                            </tr>
                            <tr>
                                <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">Noeviction</td>
                                <td class="px-6 py-4 text-sm text-gray-500">不淘汰任何键值对，当内存已满，只支持读，不支持写</td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </div>
        </section>

        <!-- Cache Eviction Algorithms -->
        <section class="mt-12">
            <h2 class="section-title text-3xl font-bold mb-6">7. 缓存淘汰算法</h2>
            
            <div class="grid md:grid-cols-2 gap-6">
                <!-- FIFO -->
                <div class="card bg-white rounded-xl p-6">
                    <h3 class="font-bold text-xl mb-3 flex items-center">
                        <i class="fas fa-sort-amount-down mr-2 text-blue-500"></i>
                        FIFO算法
                    </h3>
                    <p class="text-gray-600">
                        First in First out，先进先出。原则：一个数据最先进入缓存中，则应该最早淘汰掉。也就是说，当缓存满的时候，应当把最先进入缓存的数据给淘汰掉。
                    </p>
                </div>
                
                <!-- LFU -->
                <div class="card bg-white rounded-xl p-6">
                    <h3 class="font-bold text-xl mb-3 flex items-center">
                        <i class="fas fa-sort-numeric-down-alt mr-2 text-purple-500"></i>
                        LFU算法
                    </h3>
                    <p class="text-gray-600">
                        Least Frequently Used，最不经常使用算法。根据数据项被使用的频率来决定淘汰哪些数据。
                    </p>
                </div>
                
                <!-- LRU -->
                <div class="card bg-white rounded-xl p-6">
                    <h3 class="font-bold text-xl mb-3 flex items-center">
                        <i class="fas fa-history mr-2 text-green-500"></i>
                        LRU算法
                    </h3>
                    <p class="text-gray-600">
                        Least Recently Used，近期最少使用算法。根据数据项最后一次被使用的时间来决定淘汰哪些数据。
                    </p>
                </div>
                
                <!-- Random -->
                <div class="card bg-white rounded-xl p-6">
                    <h3 class="font-bold text-xl mb-3 flex items-center">
                        <i class="fas fa-random mr-2 text-yellow-500"></i>
                        随机淘汰
                    </h3>
                    <p class="text-gray-600">
                        随机选择要淘汰的数据项，实现简单但效果不如LFU和LRU。
                    </p>
                </div>
            </div>
            
            <div class="card bg-white rounded-xl p-6 mt-6">
                <h3 class="font-bold text-xl mb-3 text-gray-800">LRU和LFU的区别</h3>
                <p class="text-gray-600">
                    LFU算法是根据在一段时间里数据项被使用的次数选择出最少使用的数据项，即根据使用次数的差异来决定。
                    而LRU是根据使用时间的差异来决定的。
                </p>
            </div>
        </section>

        <!-- Summary Diagram -->
        <section class="mt-16">
            <h2 class="section-title text-3xl font-bold mb-8">缓存问题总结</h2>
            
            <div class="diagram-container">
                <div class="mermaid">
                    graph TD
                        A[缓存问题] --> B[缓存穿透]
                        A --> C[缓存击穿]
                        A --> D[缓存雪崩]
                        
                        B --> B1[查询不存在数据]
                        B --> B2[攻击导致DB压力]
                        B --> B3[解决方案: 空值/拦截/布隆过滤]
                        
                        C --> C1[热点数据失效]
                        C --> C2[并发访问DB]
                        C --> C3[解决方案: 永不过期/锁机制]
                        
                        D --> D1[批量缓存失效]
                        D --> D2[周期性压力波峰]
                        D --> D3[解决方案: 随机过期/永不过期]
                </div>
            </div>
        </section>
    </main>

    <script>
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            }
        });
    </script>
</body>
</html>
```